﻿using Microsoft.Win32;
using System.ComponentModel;

namespace System.Windows.Forms
{
	public partial class ListViewEx : ListView
	{
		private bool collapsible = false;
		private ListViewGroupEx defaultGroup;
		private bool disposingImageList = false;
		private ListViewGroupExCollection groups;
		private ImageList imageListGroup;

		[DefaultValue(false), Category("Behavior")]
		public bool CollapsibleGroups
		{
			get { return collapsible; }
			set
			{
				if (value != collapsible)
				{
					collapsible = value;
					SetAllGroupState(NativeMethods.ListViewGroupState.Collapsible | NativeMethods.ListViewGroupState.Normal, collapsible);
				}
			}
		}

		[DefaultValue((string)null), Category("Behavior")]
		public ImageList GroupHeaderImageList
		{
			get { return imageListGroup; }
			set
			{
				if (imageListGroup != value)
				{
					if (this.imageListGroup != null)
					{
						this.imageListGroup.RecreateHandle -= this.GroupImageListRecreateHandle;
						this.imageListGroup.Disposed -= this.DetachImageList;
					}
					this.imageListGroup = value;
					if (value != null)
					{
						value.RecreateHandle += this.GroupImageListRecreateHandle;
						value.Disposed += this.DetachImageList;
					}
					GroupImageListRecreateHandle(imageListGroup, EventArgs.Empty);
					if (!this.disposingImageList)
						UpdateListViewItemsLocations();
				}
			}
		}

		[MergableProperty(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Category("Behavior"), Localizable(true), Editor(typeof(ListViewGroupExCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public new ListViewGroupExCollection Groups
		{
			get
			{
				if (this.groups == null)
					this.groups = new ListViewGroupExCollection(this);
				return this.groups;
			}
		}

		internal ListViewGroupEx DefaultGroup
		{
			get
			{
				if (this.defaultGroup == null)
					this.defaultGroup = new ListViewGroupEx();
				return this.defaultGroup;
			}
		}

		internal bool GroupsEnabled
		{
			get
			{
				return (((this.ShowGroups && (this.groups != null)) && ((this.groups.Count > 0) && this.ComctlSupportsVisualStyles)) && !this.VirtualMode);
			}
		}

		internal int GetGroupId(int groupIndex)
		{
			NativeMethods.LVGROUP mgroup = new NativeMethods.LVGROUP(NativeMethods.ListViewGroupMask.GroupId);
			try
			{
				if (0 != SendMessage(NativeMethods.ListViewMessage.GetGroupInfoByIndex, groupIndex, mgroup))
					return mgroup.ID;
			}
			catch { }
			return -1;
		}

		internal NativeMethods.ListViewGroupState GetGroupState(ListViewGroupEx group, NativeMethods.ListViewGroupState stateMask = (NativeMethods.ListViewGroupState)0xFF)
		{
			return (NativeMethods.ListViewGroupState)SendMessage(NativeMethods.ListViewMessage.GetGroupState, GetGroupId(this.Groups.IndexOf(group)), new IntPtr((int)stateMask));
		}

		internal void InsertGroupInListView(int index, ListViewGroupEx group)
		{
			bool flag = (this.groups.Count == 1) && this.GroupsEnabled;
			this.UpdateGroupView();
			this.EnsureDefaultGroup();
			this.InsertGroupNative(index, group);
			if (flag)
			{
				for (int i = 0; i < this.Items.Count; i++)
				{
					ListViewItem item = this.Items[i];
					if (item.Group == null)
						UpdateStateToListView(item.Index);
				}
			}
		}

		internal void RemoveGroupFromListView(ListViewGroupEx group)
		{
			this.EnsureDefaultGroup();
			foreach (ListViewItem item in group.Items)
			{
				if (item.ListView == this)
					UpdateStateToListView(item.Index);
			}
			this.RemoveGroupNative(group);
			this.UpdateGroupView();
		}

		internal void SetGroupState(ListViewGroupEx group, NativeMethods.ListViewGroupState item, bool value)
		{
			NativeMethods.LVGROUP mgroup = new NativeMethods.LVGROUP(NativeMethods.ListViewGroupMask.State);
			mgroup.SetState(item, value);
			SendMessage(NativeMethods.ListViewMessage.SetGroupInfo, GetGroupId(this.Groups.IndexOf(group)), mgroup);
		}

		internal void UpdateGroupNative(ListViewGroupEx group, bool invalidate = true)
		{
			using (NativeMethods.LVGROUP nGroup = new NativeMethods.LVGROUP(group))
			{
				nGroup.SetState(NativeMethods.ListViewGroupState.Collapsible, collapsible);
				SendMessage(NativeMethods.ListViewMessage.SetGroupInfo, group.ID, nGroup);
			}
			if (invalidate)
				base.Invalidate();
		}

		internal void UpdateGroupView()
		{
			if (base.IsHandleCreated && this.ComctlSupportsVisualStyles)
				SendMessage(NativeMethods.ListViewMessage.EnableGroupView, this.GroupsEnabled ? 1 : 0, IntPtr.Zero);
		}

		private void DetachImageList(object sender, EventArgs e)
		{
			this.disposingImageList = true;
			try
			{
				this.GroupHeaderImageList = null;
			}
			finally
			{
				this.disposingImageList = false;
			}
			this.UpdateListViewItemsLocations();
		}

		private void EnsureDefaultGroup()
		{
			if ((base.IsHandleCreated && this.ComctlSupportsVisualStyles) && (this.GroupsEnabled && (SendMessage(NativeMethods.ListViewMessage.HasGroup, this.DefaultGroup.ID, IntPtr.Zero) == 0)))
			{
				this.UpdateGroupView();
				this.InsertGroupNative(0, this.DefaultGroup);
			}
		}

		private void GroupImageListRecreateHandle(object sender, EventArgs e)
		{
			if (base.IsHandleCreated)
			{
				IntPtr lparam = (this.GroupHeaderImageList == null) ? IntPtr.Zero : this.GroupHeaderImageList.Handle;
				SendMessage(NativeMethods.ListViewMessage.SetImageList, 3, lparam);
			}
		}

		private void InsertGroupNative(int index, ListViewGroupEx group)
		{
			NativeMethods.LVGROUP lParam = new NativeMethods.LVGROUP(group);
			SendMessage(NativeMethods.ListViewMessage.InsertGroup, index, lParam);
		}

		private void OnHandleCreated_Groups(EventArgs e)
		{
			if (this.groups != null)
			{
				for (int i = 0; i < this.groups.Count; i++)
					UpdateGroupNative(this.groups[i], false);
			}
		}

		private void RemoveGroupNative(ListViewGroupEx group)
		{
			SendMessage(NativeMethods.ListViewMessage.RemoveGroup, group.ID, IntPtr.Zero);
		}

		private int SendMessage(NativeMethods.ListViewMessage msg, int wParam, NativeMethods.LVGROUP group)
		{
			return NativeMethods.SendMessage(this.Handle, msg, wParam, group).ToInt32();
		}

		private void SetAllGroupState(NativeMethods.ListViewGroupState state, bool on = true)
		{
			NativeMethods.LVGROUP group = new NativeMethods.LVGROUP(NativeMethods.ListViewGroupMask.State);
			group.SetState(state, on);
			//group.Subtitle = "Dog";

			for (int i = 0; i < this.Groups.Count; i++)
				SendMessage(NativeMethods.ListViewMessage.SetGroupInfo, i, group);

			using (NativeMethods.LVGROUP grp = new NativeMethods.LVGROUP(NativeMethods.ListViewGroupMask.Subtitle))
			{
				int res = SendMessage(NativeMethods.ListViewMessage.GetGroupInfoByIndex, 0, grp);
				if (res >= 0)
					System.Diagnostics.Debug.WriteLine(grp.Subtitle);
			}

			RecreateHandle();
		}
	}
}